/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.repository.adaptor.method.getvaluegen;

import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.utils.ExpressionUtils;
import com.inspur.edp.cef.api.message.CefException;
import com.inspur.edp.cef.designtime.api.IGspCommonDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspElementDataType;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.generator.overall.GeneratorUtil;
import com.inspur.edp.cef.generator.repository.dac.DataTypeDacGenContext;
import com.inspur.edp.cef.repository.dbcolumninfo.DbColumnInfoCollection;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import java.util.ArrayList;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.ThrowStatement;

public class GetValueGenerator extends ClassMethodGenerator {

    private static final String ColNameParam = "colName";
    private static final String DataParam = "data";
//    private DbColumnInfoCollection dbColumns;
    private DataTypeDacGenContext dacContext;

    private IGspCommonDataType gspCommonDataType;

    //object getPersistenceValue(string colName, ICefData data);
    public GetValueGenerator(DataTypeDacGenContext dacContext, DbColumnInfoCollection dbColumns,
                             IGspCommonDataType gspCommonDataType) {
        this.dacContext = dacContext;
//        this.dbColumns = dbColumns;
        this.gspCommonDataType = gspCommonDataType;
    }

    @Override
    protected String getMethodName() {
        return "getPersistenceValue";
    }

    @Override
    protected TypeInfo getReturnType() {
        return new TypeInfo(Object.class);
    }

    @Override
    protected boolean getIsOverride() {
        return true;
    }


    @Override
    protected ArrayList<Modifier.ModifierKeyword> getAccessModifiers() {
        return new java.util.ArrayList<Modifier.ModifierKeyword>(java.util.Arrays
                .asList(new Modifier.ModifierKeyword[]{Modifier.ModifierKeyword.PUBLIC_KEYWORD}));
    }

    @Override
    protected ArrayList<ParameterInfo> getParameterCollection() {
        ArrayList<ParameterInfo> list = new ArrayList<ParameterInfo>();
        ParameterInfo parameterInfo = new ParameterInfo();
        parameterInfo.setParamName(ColNameParam);
        parameterInfo.setParamType(new TypeInfo(String.class));
        list.add(parameterInfo);
        parameterInfo = new ParameterInfo();
        parameterInfo.setParamName(DataParam);
        parameterInfo.setParamType(new TypeInfo(ICefData.class));
        list.add(parameterInfo);
        return list;
    }

    @Override
    protected Block buildMethodBody() {
        Block block = ast.newBlock();
        SwitchStatement switchStatement = ast.newSwitchStatement();
        addSwitchExpression(switchStatement);
        addSwitchCases(switchStatement);

        block.statements().add(switchStatement);
        return block;
    }

    private void addSwitchExpression(SwitchStatement switchStatement) {
        switchStatement.setExpression(ast.newSimpleName(ColNameParam));
    }

    private void addSwitchCases(SwitchStatement switchStatement) {
        for(IGspCommonField gspCommonField: gspCommonDataType.getContainElements()){
            addSwitchCase(switchStatement, gspCommonField);
        }
        addDefaultCase(switchStatement);
    }

    private void addSwitchCase(SwitchStatement switchStatement, IGspCommonField field) {
        if (field.getIsRefElement()) {
            return;
        }
        if (field.getIsUdt()) {
            UnifiedDataTypeDef udt = (UnifiedDataTypeDef)dacContext.getReposContext().getCefContext().getCefGenContext().
                    getMetadataJitContext().getMetadata(field.getUdtID()).getContent();
            if (udt instanceof SimpleDataTypeDef || (udt instanceof ComplexDataTypeDef && ((ComplexDataTypeDef)udt).getDbInfo().getMappingType() == ColumnMapType.SingleColumn)) {
                addUdtCase(switchStatement, field.getLabelID(), udt, field, field);
            }
            else {
                for(IGspCommonField child: field.getChildElements()){
                    IGspCommonField refUdtField = null;
                    for(IGspCommonField udtField: udt.getContainElements()){
                        if(field.getMappingRelation().getMappingInfo(child.getID()).equals(udtField.getID())){
                            refUdtField = udtField;
                            break;
                        }
                    }
                    addUdtCase(switchStatement, child.getLabelID(), udt, field, refUdtField);
                }
            }
        }
        else {
            addOwnPropertyCase(switchStatement, field);
        }
//		return getOwnPropSection(columnInfo.getColumnName());
    }

    private void addUdtCase(SwitchStatement switchStatement, String caseLabel, UnifiedDataTypeDef udt, IGspCommonField belongField, IGspCommonField gspCommonField) {
        SwitchCase caseStatement = ast.newSwitchCase();
        caseStatement.setExpression(GeneratorUtil.getStringLiteral(ast, caseLabel));
        switchStatement.statements().add(caseStatement);
        String udtEleLabel = gspCommonField.getLabelID();
        if (udt instanceof SimpleDataTypeDef || (udt instanceof ComplexDataTypeDef && ((ComplexDataTypeDef)udt).getDbInfo().getMappingType() == ColumnMapType.SingleColumn)) {
            udtEleLabel = udt.getCode();
        }
        addUdtCaseReturnStatement(switchStatement, udtEleLabel, udt, belongField, gspCommonField);
    }

    private void addUdtCaseReturnStatement(SwitchStatement switchStatement, String udtEleLabel,
                                           UnifiedDataTypeDef udt, IGspCommonField belongField, IGspCommonField gspCommonField) {
        MethodInvocation methodInvocation = ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName("getPersistenceValue"));
        addgetPersistenceValueExpression(methodInvocation, udt);
        addgetPersistenceValueArguments(methodInvocation, udtEleLabel, belongField);

        ReturnStatement returnStatement = ast.newReturnStatement();
        returnStatement.setExpression(methodInvocation);
        switchStatement.statements().add(returnStatement);
    }

    private void addgetPersistenceValueExpression(MethodInvocation methodInvocation, UnifiedDataTypeDef udt) {
        MethodInvocation methodInvocation1 = ast.newMethodInvocation();
        methodInvocation1.setName(ast.newSimpleName("getNestedRepository"));
//        setcreateRepositoryExpression(methodInvocation1);
        methodInvocation1.arguments()
                .add(GeneratorUtil.getStringLiteral(ast, udt.getUdtType()));

        methodInvocation.setExpression(methodInvocation1);
    }

    private void setcreateRepositoryExpression(MethodInvocation methodInvocation1) {
        MethodInvocation methodInvocation2=ast.newMethodInvocation();
        methodInvocation2.setName(ast.newSimpleName("getUdtRepositoryFactory"));
        methodInvocation2.setExpression(ast.newName("com.inspur.edp.udt.api.UdtManagerUtils"));
        methodInvocation1.setExpression(methodInvocation2);
    }

    private void addgetPersistenceValueArguments(MethodInvocation methodInvocation,
                                                 String udtEleLabel, IGspCommonField belongField) {
        methodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, udtEleLabel));

        MethodInvocation methodInvocationgetProperty = ast.newMethodInvocation();
        methodInvocationgetProperty.setName(ast.newSimpleName("get" + belongField.getLabelID()));

        ParenthesizedExpression parenthesizedExpression=ast.newParenthesizedExpression();
        CastExpression castExpression=ast.newCastExpression();
        castExpression.setExpression(ast.newSimpleName(DataParam));
        String childType = dacContext.getReposContext().getCefContext().getCefGenContext().getDataInterfaceTypes().get(gspCommonDataType.getCode()).getFullName();
        castExpression.setType(new TypeInfo(childType)
                .getType(ast, dacContext.getReposContext().getCefContext()));
        parenthesizedExpression.setExpression(castExpression);
        methodInvocationgetProperty.setExpression(parenthesizedExpression);
        methodInvocation.arguments().add(methodInvocationgetProperty);

//        if(isDefaultNull(gspCommonField.getBelongObject().getBeLabel().get(0))){
        if(isDefaultNull(belongField)){
            methodInvocation.arguments().add(ast.newBooleanLiteral(true));
        }
    }

    private boolean isDefaultNull(String propName){
        //todo wangmj
        return false;
//        List<AbstractPropertyInfo> infos =  "RootNode".equals(nodeCode) ?
//            dacContext.getReposContext().getCefContext().getCurrentTypeInfo().getEntityEntityTypeInfo().getPropInfos() :
//            dacContext.getReposContext().getCefContext().getEn().getEntityEntityTypeInfos().get(nodeCode).getPropInfos();
//        if(infos == null){
//            return false;
//        }
//
//        AbstractPropertyInfo info = infos.stream().filter(item -> item.getName().equals(propName)).findFirst().orElse(null);
//        if(info == null){
//            return false;
//        }
//        return info.getIsDefaultNull();
    }

    private void addOwnPropertyCase(SwitchStatement switchStatement, IGspCommonField gspCommonField) {
        SwitchCase caseStatement = ast.newSwitchCase();
//        caseStatement.setExpression(ExpressionUtils.getStringLiteral(ast, columnInfo.getColumnName()));
        caseStatement.setExpression(ExpressionUtils.getStringLiteral(ast, gspCommonField.getLabelID()));
        switchStatement.statements().add(caseStatement);

        MethodInvocation methodInvocationTransType = ast.newMethodInvocation();
        methodInvocationTransType.setName(ast.newSimpleName("transType"));
        addTransTypeExpression(methodInvocationTransType, gspCommonField);
        addTransTypeArguments(methodInvocationTransType, gspCommonField);
        ReturnStatement returnStatement = ast.newReturnStatement();
        returnStatement.setExpression(methodInvocationTransType);
        switchStatement.statements().add(returnStatement);
    }

    private void addTransTypeExpression(MethodInvocation methodInvocationTransType,
                                        IGspCommonField gspCommonField) {
        MethodInvocation methodInvocationGetTypeProcessor = ast.newMethodInvocation();
        methodInvocationGetTypeProcessor.setName(ast.newSimpleName("getTypeTransProcesser"));
        addgetTypeProcessorExpression(methodInvocationGetTypeProcessor, gspCommonField);
        methodInvocationTransType.setExpression(methodInvocationGetTypeProcessor);
    }

    private void addgetTypeProcessorExpression(MethodInvocation methodInvocationGetTypeProcessor,
                                               IGspCommonField gspCommonField) {
        MethodInvocation methodInvocationGetItem = ast.newMethodInvocation();
        methodInvocationGetItem.setName(ast.newSimpleName("getItem"));
        methodInvocationGetItem.arguments()
                .add(GeneratorUtil.getStringLiteral(ast, gspCommonField.getLabelID()));
//                .add(GeneratorUtil.getStringLiteral(ast, columnInfo.getColumnName()));
        addgetItemExpression(methodInvocationGetItem);

        methodInvocationGetTypeProcessor.setExpression(methodInvocationGetItem);
    }

    private void addgetItemExpression(MethodInvocation methodInvocationGetItem) {
        MethodInvocation methodInvocationgetContainColumns = ast.newMethodInvocation();
        methodInvocationgetContainColumns.setName(ast.newSimpleName("getContainColumns"));
        methodInvocationgetContainColumns.setExpression(ast.newThisExpression());
        methodInvocationGetItem.setExpression(methodInvocationgetContainColumns);
    }

    private void addTransTypeArguments(MethodInvocation methodInvocationTransType,
                                       IGspCommonField gspCommonField) {
        MethodInvocation methodInvocation = ast.newMethodInvocation();
//        methodInvocation.setName(ast.newSimpleName("get" + columnInfo.getColumnName()));
        methodInvocation.setName(ast.newSimpleName("get" + gspCommonField.getLabelID()));
        String childType = dacContext.getReposContext().getCefContext().getCefGenContext().getDataInterfaceTypes().get(gspCommonDataType.getCode()).getFullName();
        ParenthesizedExpression parenthesizedExpression = ast.newParenthesizedExpression();
        CastExpression castExpression = ast.newCastExpression();
        castExpression.setExpression(ast.newSimpleName(DataParam));
        TypeInfo info =new TypeInfo(childType);
//        if(IUdtData.class.isAssignableFrom(childType))
          info.setUseFullName(true);
        castExpression.setType(info
                .getType(ast, dacContext.getReposContext().getCefContext().getCefGenContext().getCoreGenContext()));
        parenthesizedExpression.setExpression(castExpression);

        methodInvocation.setExpression(parenthesizedExpression);
        methodInvocationTransType.arguments().add(methodInvocation);
        if((!gspCommonField.getIsUdt())&&!(gspCommonField.getObjectType() == GspElementObjectType.Enum)&&
            (gspCommonField.getMDataType() == GspElementDataType.Integer
                ||gspCommonField.getMDataType()== GspElementDataType.Decimal
                ||gspCommonField.getMDataType()== GspElementDataType.String||gspCommonField.getMDataType()== GspElementDataType.Boolean)){
            if(isDefaultNull(gspCommonField)) {
                BooleanLiteral booleanLiteral = ast.newBooleanLiteral(true);
                methodInvocationTransType.arguments().add(booleanLiteral);
            }
//            BooleanLiteral booleanLiteral= ast.newBooleanLiteral(true);
//            methodInvocationTransType.arguments().add(booleanLiteral);

        }
    }
    private static boolean isDefaultNull(IGspCommonField element) {
        return element != null && element instanceof GspBizEntityElement
            && ((GspBizEntityElement) element).getIsDefaultNull();
    }
    private void addDefaultCase(SwitchStatement switchStatement) {
        SwitchCase defaultCase = ast.newSwitchCase();
        defaultCase.setExpression(null);
        switchStatement.statements().add(defaultCase);
        addImport(CefException.class.getTypeName());
        ThrowStatement throwStatement = GeneratorUtil.getThrowStatement(ast,null);
//        ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
//
//        classInstanceCreation.setType(
//                new com.inspur.edp.caf.generator.baseInfo.TypeInfo(RuntimeException.class).getType(ast));
//        throwStatement.setExpression(classInstanceCreation);
        switchStatement.statements().add(throwStatement);
    }
}
